/*rebuild.cpp
 *This program rebuilds RAID if one of disks fails.
 *Created on: 2013.12.24
 *Author: Petrunin D.S.
 */
#include <iostream>
#include <stdio.h>
#include <cstring>
#include "base.h"


int rebuildRAID1(int nDisks, int nBlocks, int blockS) {
	mapping(nDisks, nBlocks, blockS, true);
	int failed = 0;
	for (int i = 1; i <= nDisks; i++) {
		if (mappedDataArray[i] == NULL) failed++;
	}
	if (failed == nDisks) {
		cout << "All of disks are failed. Can't rebuild RAID.\n";
		return 1;
	}
	int validDisk;
	for(int i = 1; i <= nDisks; i++) {
		if (mappedDataArray[i] != NULL) {validDisk = i; break;}
	}

	for (int i = 1; i <= nDisks; i++) {
		if ((mappedDataArray[i] != NULL)&&(i != validDisk)) {
			if (memcmp(mappedDataArray[i],mappedDataArray[validDisk], nBlocks*blockS) != 0) {
				cout << "Disks have different data. Can't rebuild RAID.\n";
				return 1;
			}
		}
	}

	FILE* disk;
	char DiskNm[10];
	for (int i = 1; i <= nDisks; i++) {
		if (mappedDataArray[i] == NULL) {
			sprintf(DiskNm, "Disk%i", i);
			disk = fopen(DiskNm, "wb+");
			if (disk == NULL) {
				cout << "Can't create Disk" << i << ". Can't rebuild RAID.\n";
				return 1;
			}
			fwrite(mappedDataArray[validDisk], sizeof(char), blockS * nBlocks, disk);
			fclose(disk);
		}
	}

	unmapping(nDisks, nBlocks, blockS, true);
	return 0;
}


int rebuildRAID345(int nDisks, int nBlocks, int blockS) {
	mapping(nDisks, nBlocks, blockS, true);
	int failed = 0;
	for (int i = 1; i <= nDisks; i++) {
		if (mappedDataArray[i] == NULL) failed++;
	}
	if (failed > 1) {
		cout << "More than 1 of disks are failed. Can't rebuild RAID.\n";
		return 1;
	}

	int failedDisk;
	for (int i = 1; i <= nDisks; i++) {
		if (mappedDataArray[i] == NULL) {failedDisk = i; break;}
	}
	char DiskNm[10];
	sprintf(DiskNm, "Disk%i", failedDisk);
	FILE* disk = fopen(DiskNm, "wb+");
	if (disk == NULL) {
		cout << "Can't create Disk" << failedDisk << ". Can't rebuild RAID.\n";
		return 1;
	}

	char XORres;
	for (int i = 0; i < nBlocks * blockS; i++) {
		XORres = 0;
		for (int j = 1; j <= nDisks; j++) {
			if (j != failedDisk) {
				XORres = XORres ^ *(mappedDataArray[j] + i);
			}
		}
		fwrite(&XORres,sizeof(char),1,disk);
	}
	fclose(disk);
	unmapping(nDisks, nBlocks, blockS, true);
	return 0;
}


int main(int argc, char* argv[]) {

	FILE* config = fopen("config", "r");
	if (config == NULL) {
		cout << "Can't open config file.\n";
		return 1;
	}
	int nDisks, nBlocks, blockS, RAIDLevel;
	fscanf(config, "%i\n%i\n%i\n%i\n", &nDisks, &nBlocks, &blockS, &RAIDLevel);
	fclose(config);
	if (!failedDisk(nDisks, true)) {
		cout << "No need to rebuild RAID.\n";
		return 1;
	}

	switch (RAIDLevel) {
	case 0:
		cout << "RAID0 can't be rebuilded.\n";
		return 1;
	case 1:
		return rebuildRAID1(nDisks, nBlocks, blockS);
	case 3:
		return rebuildRAID345(nDisks, nBlocks, blockS);
	case 4:
		return rebuildRAID345(nDisks, nBlocks, blockS);
	case 5:
		return rebuildRAID345(nDisks, nBlocks, blockS);
	}
}

